#include "network/buffer.hpp"

#include "base/assert.hpp"

#include <cstring>
#include <memory>
#include <utility>

namespace ibox::network {
Buffer::Buffer(size_t reverse_size) {
    // 如果预留空间大小，那么要为缓冲分配空间
    if (reverse_size > 0) {
        m_buffer_ptr = std::make_unique<uint8_t[]>(reverse_size);
        m_buffer_size = reverse_size;
    }
}

Buffer::Buffer(const Buffer &other) { clone_from(other); }

Buffer::Buffer(Buffer &&other) noexcept { swap(other); }

Buffer &Buffer::operator=(const Buffer &other) {
    if (this != &other) {
        clone_from(other);
    }

    return *this;
}

Buffer &Buffer::operator=(Buffer &&other) noexcept {
    if (this != &other) {
        reset();
        swap(other);
    }

    return *this;
}

void Buffer::swap(Buffer &other) {
    if (&other == this) {
        return;
    }

    std::swap(other.m_buffer_ptr, m_buffer_ptr);
    std::swap(other.m_buffer_size, m_buffer_size);
    std::swap(other.m_read_index, m_read_index);
    std::swap(other.m_write_index, m_write_index);
}

void Buffer::reset() {
    Buffer tmp(0);
    swap(tmp);
}

bool Buffer::ensure_writable_size(size_t write_size) {
    if (write_size == 0) {
        return true;
    }

    // 空间足够
    if (writable_size() >= write_size) {
        return true;
    }

    // 检查是否可以通过将数据往前挪是否可以满足空间要求
    if ((writable_size() + m_read_index) >= write_size) {
        // 将 readable 区的数据往前移
        ::memmove(m_buffer_ptr.get(),
                  (m_buffer_ptr.get() + m_read_index),
                  (m_write_index - m_read_index));
        m_write_index -= m_read_index;
        m_read_index = 0;
        return true;
    }
    // 只有重新分配更多的空间才可以
    size_t new_size = (m_write_index + write_size) << 1; // 两倍扩展
    std::unique_ptr<uint8_t[]> new_buff = std::make_unique<uint8_t[]>(new_size);
    if (new_buff == nullptr) {
        return false;
    }

    if (m_buffer_ptr != nullptr) {
        // 只需要复制 readable 部分数据
        ::memcpy((new_buff.get() + m_read_index),
                 (m_buffer_ptr.get() + m_read_index),
                 (m_write_index - m_read_index));
    }

    m_buffer_ptr = std::move(new_buff);
    m_buffer_size = new_size;

    return true;
}

void Buffer::has_written(size_t write_size) {
    if (m_write_index + write_size > m_buffer_size) {
        m_write_index = m_buffer_size;
    } else {
        m_write_index += write_size;
    }
}

size_t Buffer::append(const void *p_data, size_t data_size) {
    if (ensure_writable_size(data_size)) {
        ::memcpy(writable_begin(), p_data, data_size);
        has_written(data_size);
        return data_size;
    }
    return 0;
}

void Buffer::has_read(size_t read_size) {
    if (m_read_index + read_size > m_write_index) {
        m_read_index = m_write_index = 0;
    } else {
        m_read_index += read_size;
        if (m_read_index == m_write_index) {
            m_read_index = m_write_index = 0;
        }
    }
}

// TODO: 改个更符合语义的名字
void Buffer::has_readall() { m_read_index = m_write_index = 0; }

size_t Buffer::fetch(void *p_buff, size_t buff_size) {
    size_t read_size
        = (buff_size > readable_size()) ? readable_size() : buff_size;
    ::memcpy(p_buff, readable_begin(), read_size);
    has_read(read_size);
    return read_size;
}

// 不是完全复制，只复制有效的数据
void Buffer::clone_from(const Buffer &other) {
    // 如果 other 有可读数据，则要根据可读大小分配空间
    if (other.readable_size() > 0) {
        std::unique_ptr<uint8_t[]> p_buff
            = std::make_unique<uint8_t[]>(other.readable_size());
        expect(p_buff != nullptr);
        ::memcpy(p_buff.get(), other.readable_begin(), other.readable_size());
        m_buffer_ptr = std::move(p_buff);
        m_buffer_size = m_write_index = other.readable_size();

    } else {
        m_buffer_ptr = nullptr;
        m_buffer_size = m_write_index = 0;
    }

    m_read_index = 0;
}

void Buffer::shrink() {
    Buffer tmp(*this); // 将自己复制给 tmp，其间会缩减空间
    swap(tmp);         // 与 tmp 交换
}
} // namespace ibox::network
